#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>

#include "od_engine.h"
#include "udp_socket.h"
#include "od_rtp_unpack.h"
#include "od_ts_unpack.h"
#include "od_udp_recv.h"
#include "od_framework.h"
#include "param_json.h"
#include "rpc_response.h"
#include "decoder_param.h"
#include "HisiDecEncCtrl.h"
#include "HisiDecSendStream.h"
#include "HisiDecVpssOutDynamicSet.h"
#include "sample_comm.h"
#include "params_id_def.h"
#include "retrans_send.h"

#define DEFAULT_RECV_MULTI_IP  "230.1.1.30"
//static char default_mac[] = "00:0A:35:00:22:01";
static char software_date[32] = "Build@"__DATE__" "__TIME__;
static char software_version[32] = "V1.0.0";


#define USER_PARAM_END {PT_NULL_, 0, 0, 0, "", 0, 0, JSPARA_ARG_NULL, NULL}

/** int 类型通用设置*/
#define PARAM_SET_INT_COMN(h, param_id, mem, msg)  do{ \
                                                        int value, check; \
                                                        value = *(int *)mem; \
                                                        if (!h) break; \
                                                        od_task_set_para(h, param_id, &value); \
                                                        od_task_app(h); \
                                                        od_task_get_para(h, param_id, &check); \
                                                        if (check != value) { \
                                                            *(int *)mem = check; \
                                                            add_msg_to_err_table(msg); \
                                                            return OD_ERROR; \
                                                        } \
                                                    }while(0)

                                                        
/** string 类型通用设置*/
#define PARAM_SET_STR_COMN(h, param_id, mem, msg)  do{ \
                                                        char check[128] = {0}; \
                                                        char *value; \
                                                        value = (char *)mem; \
                                                        if (!h) break; \
                                                        od_task_set_para(h, param_id, value); \
                                                        od_task_app(h); \
                                                        od_task_get_para(h, param_id, check); \
                                                        if (strcmp(check, value)) { \
                                                            snprintf(value, sizeof(*value), "%s", check); \
                                                            add_msg_to_err_table(msg); \
                                                            return OD_ERROR; \
                                                        } \
                                                    }while(0)

/*设置结构体数组中int变量时使用*/
#define PARAM_SET_STRU_INT_COMN(h, param_id, value, msg)  do{ \
                                                            int check; \
                                                            if (!h) break; \
                                                            od_task_set_para(h, param_id, &value); \
                                                            od_task_app(h); \
                                                            od_task_get_para(h, param_id, &check); \
                                                            if (check != value) { \
                                                                value = check; \
                                                                add_msg_to_err_table(msg); \
                                                            } \
                                                        }while(0)

/*设置结构体数组中string变量时使用*/
#define PARAM_SET_STRU_STR_COMN(h, param_id, str, msg)  do{ \
                                                            char check[128] = {0}; \
                                                            if (!h) break; \
                                                            od_task_set_para(h, param_id, str); \
                                                            od_task_app(h); \
                                                            od_task_get_para(h, param_id, check); \
                                                            if (strcmp(check, str)) { \
                                                                snprintf(str, sizeof(*str), "%s", check); \
                                                                add_msg_to_err_table(msg); \
                                                            } \
                                                        }while(0)



static int32_t act_vid_dec_params(void *var_hdl, int offset, void *mem, int id)
{
    od_framework_t *framework = var_hdl;
    od_task_handle hisi_vpss = ghash_find(framework->task_table, TSK_HISI_DEC_ENC_CTL);
    od_task_handle hisi_himpp_set = ghash_find(framework->task_table, TSK_HISI_HIMPP_DYNAMIC_SET);

    switch(offset)
    {
        case OFFSET(decoder_vid_enc_params, enPayLoad):
            PARAM_SET_INT_COMN(hisi_vpss, HISIDECENCCTRL_ENPAYLOAD, mem, "HIMPP enPayLoad set fail!!");
            break;

        case OFFSET(decoder_vid_enc_params, enSize):
            PARAM_SET_INT_COMN(hisi_vpss, HISIDECENCCTRL_ENSIZE, mem, "HIMPP enSize set fail!!");
            break;

        case OFFSET(decoder_vid_enc_params, enVoMode):
            PARAM_SET_INT_COMN(hisi_himpp_set, HiMPP_VOMODE, mem, "HIMPP enVoMode set fail!!");
            break;

        case OFFSET(decoder_vid_enc_params, u32Gop):
            PARAM_SET_INT_COMN(hisi_vpss, HISIDECENCCTRL_U32GOP, mem, "HIMPP u32Gop set fail!!");
            break;

        case OFFSET(decoder_vid_enc_params, u32FrameRate):
            PARAM_SET_INT_COMN(hisi_vpss, HISIDECENCCTRL_U32FRAMERATE, mem, "HIMPP u32FrameRate set fail!!");
            break;

        case OFFSET(decoder_vid_enc_params, u32BitRate):
            PARAM_SET_INT_COMN(hisi_vpss, HISIDECENCCTRL_U32BITRATE, mem, "HIMPP u32BitRate set fail!!");
            break;

        case OFFSET(decoder_vid_enc_params, enRcMode):
            PARAM_SET_INT_COMN(hisi_vpss, HISIDECENCCTRL_ENRCMODE, mem, "HIMPP enRcMode set fail!!");
            break;

        case OFFSET(decoder_vid_enc_params, enDecMode):
            PARAM_SET_INT_COMN(hisi_vpss, HISIDECENCCTRL_ENDECMODE, mem, "HIMPP enDecMode set fail!!");
            break;
    }

    return OD_OK;
}

static int32_t act_send_params(void *var_hdl, int offset, void *mem, int id)
{
    od_framework_t *framework = var_hdl;
    od_task_handle h_send = ghash_find(framework->task_table, TSK_VID_RETRANS_SEND);

    switch(offset)
    {
        case OFFSET(decoder_send_params, ctrl):
            PARAM_SET_INT_COMN(h_send, RETRANSFER_SEND_ENABLE, mem, "send ctrl set fail!!");
            break;

        case OFFSET(decoder_send_params, local_ip):
            PARAM_SET_STR_COMN(h_send, RETRANSFER_SEND_LOCAL_IP, mem, "local_ip set fail!!");
            break;

        case OFFSET(decoder_send_params, send_ip):
            PARAM_SET_STR_COMN(h_send, RETRANSFER_SEND_IP, mem, "send_ip set fail!!");
            break;

        case OFFSET(decoder_send_params, send_port):
            PARAM_SET_INT_COMN(h_send, RETRANSFER_SEND_PORT, mem, "send_port set fail!!");
            break;

        case OFFSET(decoder_send_params, vbv_flg):
            PARAM_SET_INT_COMN(h_send, RETRANSFER_SEND_VBV_FLG, mem, "vbv_flg set fail!!");
            break;

        case OFFSET(decoder_send_params, sys_band):
            PARAM_SET_INT_COMN(h_send, RETRANSFER_SEND_SYS_BAND, mem, "sys_band set fail!!");
            break;

        case OFFSET(decoder_send_params, bitrate):
            PARAM_SET_INT_COMN(h_send, RETRANSFER_SEND_BITRATES, mem, "bitrate set fail!!");
            break;
    }

    return OD_OK;
}


static int32_t act_decoder_params(void *var_hdl, int offset, void *mem, int chn)
{
    od_framework_t * framework = var_hdl;
    decoder_params_obj *json_param = framework->param_table;
    decoder_params * params = &(json_param->dec_params);
    int32_t ret = OD_OK;

    RPTDBG("offset is %d", offset);
    switch (offset)
    {
        case OFFSET(decoder_params, exit):
            /*mem match*/
            if((&params->exit) == mem)
            {
                params->done = params->exit;
            }
            else
            {
                add_msg_to_err_table("exit param not match");
                RPTERR("exit param not match");
                ret = OD_ERROR;
            }
            break;

        case OFFSET(decoder_params, recv):
            {
                int idx = CLIP(0, DECODER_MAX_CHN, chn);
                char *ptr = NULL;
                TSK_UDP_RECV_VID_(ptr, idx);
                od_task_handle udp_recv_hdl = ghash_find(framework->task_table, ptr);
                TSK_RTP_UNPACK_(ptr, idx);
                od_task_handle rtp_recv_hdl = ghash_find(framework->task_table, ptr);
                
                decoder_vid_recv_params *pRecv = &params->recv[idx];
                
                PARAM_SET_STRU_INT_COMN(udp_recv_hdl, UDP_RECV_ENABLE, pRecv->ctrl, "dec_ctrl set fail");
                PARAM_SET_STRU_INT_COMN(udp_recv_hdl, UDP_RECV_RECV_PORT, pRecv->vid_port, "udp recv port set fail");
                PARAM_SET_STRU_INT_COMN(rtp_recv_hdl, RTP_UNPACK_AVC_PT, pRecv->vid_pt, "avc rtp payload type set fail!!");
                PARAM_SET_STRU_INT_COMN(udp_recv_hdl, UDP_RECV_CAST_MODE, pRecv->recv_cast_mode, "recv_cast_mode type set fail!!");
                PARAM_SET_STRU_STR_COMN(udp_recv_hdl, UDP_RECV_MULTI_ADDR, pRecv->recv_multi_addr, "recv_multi_addr set fail!!");
                od_task_get_para(rtp_recv_hdl, RTP_UNPACK_VIDEO_LOST_RATE, &pRecv->vid_lost_rate);
            }
            break;

        default:
            break;
    }
    return ret;
}



struct pjdesc pd_dec_recv[] = {
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_recv_params, ctrl),          DEC_VRCV_CTRL,    sizeof(int), 0, JSPARA_ARG_S32(OD_ON, OD_OFF, OD_ON), NULL},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_recv_params, vid_port),      DEC_VRCV_VIDPORT, sizeof(int), 0, JSPARA_ARG_S32(3000, 1026, 65536), NULL},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_recv_params, vid_pt),        DEC_VRCV_VIDPT,   sizeof(int), 0, JSPARA_ARG_S32(96, 0, 127), NULL},
    {PT_SINT_ | PT_FMT_RDONLY, 0, 1, OFFSET(decoder_vid_recv_params, vid_lost_rate), DEC_VRCV_VIDLOSTRATE, sizeof(int), 0, JSPARA_ARG_S32(0, 0, 100), NULL},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_recv_params, recv_cast_mode),  DEC_VRCV_RECVCASTMODE, sizeof(int), 0, JSPARA_ARG_S32(UDP_SOCKET_UNICAST, UDP_SOCKET_UNICAST, UDP_SOCKET_MULTICAST), NULL},
    {PT_STRI_, 0, 1, OFFSET(decoder_vid_recv_params, recv_multi_addr), DEC_VRCV_RECVMULTIADDR, 32, 0, JSPARA_ARG_STR(DEFAULT_RECV_MULTI_IP), NULL},
    USER_PARAM_END
};

hash_desc_t param_dec_recv =
{
    .pjdesc = pd_dec_recv,
    .hash_tab = NULL,
};


struct pjdesc pd_vid_dec[] = {
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_enc_params, enPayLoad), DEC_VENC_ENPAYLOAD, sizeof(int), 0, JSPARA_ARG_S32(PT_H264, PT_PCMU, PT_BUTT-1), act_vid_dec_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_enc_params, enSize),    DEC_VENC_ENSIZE,    sizeof(int), 0, JSPARA_ARG_S32(PIC_1080P, PIC_CIF, PIC_BUTT-1), act_vid_dec_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_enc_params, enVoMode),  DEC_VENC_ENVOMODE,  sizeof(int), 0, JSPARA_ARG_S32(VO_MODE_1MUX, VO_MODE_1MUX, VO_MODE_BUTT-1), act_vid_dec_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_enc_params, u32Gop),    DEC_VENC_U32GOP,    sizeof(int), 0, JSPARA_ARG_S32(1, 30, 100), act_vid_dec_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_enc_params, u32FrameRate), DEC_VENC_U32FRAMERATE, sizeof(int), 0, JSPARA_ARG_S32(30, 1, 60), act_vid_dec_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_enc_params, u32BitRate),   DEC_VENC_U32BITRATE,   sizeof(int), 0, JSPARA_ARG_S32(1024, 64, (120 * 1024)), act_vid_dec_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_enc_params, enRcMode),     DEC_VENC_ENRCMODE,     sizeof(int), 0, JSPARA_ARG_S32(VENC_RC_MODE_H264CBR, VENC_RC_MODE_H264CBR, VENC_RC_MODE_BUTT-1), act_vid_dec_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_vid_enc_params, enDecMode),    DEC_VENC_ENDECMODE,    sizeof(int), 0, JSPARA_ARG_S32(VIDEO_DEC_MODE_IP, VIDEO_DEC_MODE_IPB, VIDEO_DEC_MODE_BUTT-1), act_vid_dec_params},
    USER_PARAM_END
};

hash_desc_t param_vid_dec =
{
    .pjdesc = pd_vid_dec,
    .hash_tab = NULL,
};

struct pjdesc pd_send[] = {
    {PT_SINT_, 0, 1, OFFSET(decoder_send_params, ctrl),     DEC_SEND_CTRL,      sizeof(int), 0, JSPARA_ARG_S32(OD_ON, OD_OFF, OD_ON), act_send_params},
    {PT_STRI_, 0, 1, OFFSET(decoder_send_params, local_ip), DEC_SEND_LOCAL_IP,  32, 0, JSPARA_ARG_STR("172.16.60.167"), act_send_params},
    {PT_STRI_, 0, 1, OFFSET(decoder_send_params, send_ip),  DEC_SEND_SEND_IP,   32, 0, JSPARA_ARG_STR("172.16.60.63"), act_send_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_send_params, send_port),DEC_SEND_SEND_PORT, sizeof(int), 0, JSPARA_ARG_S32(4000, 1026, 65536), act_send_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_send_params, vbv_flg),  DEC_SEND_VBV_FLG,   sizeof(int), 0, JSPARA_ARG_S32(OD_ON, OD_OFF, OD_ON), act_send_params},
    {PT_SINT_, 0, 1, OFFSET(decoder_send_params, sys_band), DEC_SEND_SYS_BAND,  sizeof(int), 0, JSPARA_ARG_S32(6000, 64, 20*1024), act_send_params},
    {PT_SINT_ | PT_FMT_RDONLY, 0, 1, OFFSET(decoder_send_params, bitrate),  DEC_SEND_BITRATE,   sizeof(int), 0, JSPARA_ARG_S32(6000, 64, 20*1024), act_send_params},
    USER_PARAM_END
};

hash_desc_t param_send =
{
    .pjdesc = pd_send,
    .hash_tab = NULL,
};



struct pjdesc pd_usrset[] = {
    {PT_STRU_, 0, DECODER_MAX_CHN, OFFSET(decoder_params, recv), DEC_VRCV, sizeof(decoder_vid_recv_params), (size_t)&param_dec_recv, JSPARA_ARG_NULL, act_decoder_params},
    {PT_STRU_, 0, 1, OFFSET(decoder_params, vdec), DEC_VENC, sizeof(decoder_vid_enc_params), (size_t)&param_vid_dec, JSPARA_ARG_NULL, NULL},
    {PT_STRU_, 0, 1, OFFSET(decoder_params, send), DEC_SEND, sizeof(decoder_send_params), (size_t)&param_send, JSPARA_ARG_NULL, NULL}, 
    {PT_SINT_ | PT_FMT_NOSAVE, 0, 1, OFFSET(decoder_params, exit), "exit", sizeof(int), 0, JSPARA_ARG_S32(OD_ON, OD_OFF, OD_ON), act_decoder_params},
    {PT_STRI_ | PT_FMT_NOSAVE, 0, 1, OFFSET(decoder_params, version), "version", OD_MAX_OBJ_NAME, 0, JSPARA_ARG_STR(software_version), NULL},
    {PT_STRI_ | PT_FMT_NOSAVE, 0, 1, OFFSET(decoder_params, date), "date", OD_MAX_OBJ_NAME, 0, JSPARA_ARG_STR(software_date), NULL},
    USER_PARAM_END
};


static int usrset_params_init(decoder_params *user)
{
    decoder_vid_recv_params *recv_param = NULL;
    decoder_vid_enc_params *dec_param = NULL;
    decoder_send_params *send_param = NULL;
    int idx;

    if(!user) {
        RPTERR("NULL pointer, error!"); 
        return OD_ERROR;
    }

    //dec recv
    for (idx=0; idx < DECODER_MAX_CHN; idx++) {
        recv_param = &(user->recv[idx]);
        recv_param->ctrl = OD_ON;
        recv_param->vid_port = 4000;
        recv_param->vid_pt = 96;
        recv_param->recv_cast_mode = UDP_SOCKET_MULTICAST;
        strcpy(recv_param->recv_multi_addr, DEFAULT_RECV_MULTI_IP);
    }

    //vid dec
    dec_param = &(user->vdec);
    dec_param->enPayLoad = PT_H264;
    dec_param->enSize = PIC_1080P;
    dec_param->enVoMode = VO_MODE_9MUX;
    dec_param->u32Gop = 30;
    dec_param->u32FrameRate = 30;
    dec_param->u32BitRate = 1024 * 1024;
    dec_param->enRcMode = SAMPLE_RC_CBR;
    dec_param->enDecMode = VIDEO_DEC_MODE_IP;

    //send
    send_param = &(user->send);
    send_param->ctrl = OD_ON;
    strcpy(send_param->local_ip, "172.16.60.167");
    strcpy(send_param->send_ip, "172.16.60.63");
    send_param->send_port = 6000;
    send_param->vbv_flg = OD_OFF;
    send_param->sys_band = 10000;    

    /*read only*/
    strncpy(user->version, software_version, OD_MAX_OBJ_NAME - 1); //版本
    strncpy(user->date, software_date, OD_MAX_OBJ_NAME - 1);  //编译日期

    /*如果想要结束进程，设置exit变量，在其act响应函数中操作done，控制进程结束*/
    user->exit = OD_OFF;
    user->done = OD_OFF;
    
    return OD_OK;

}

int decoder_params_init(od_framework_t *frameworks, char *filename)
{
    jsparam_args_t args;
    decoder_params_obj * params_obj = (decoder_params_obj *)(frameworks->param_table);

    usrset_params_init(&(params_obj->dec_params));
    RPTDBG("after usrset_params_init");

    params_obj->private = NULL;
    
    /****************json handle init, include  system json and usrset json table**********/
    args.mempt = &(params_obj->dec_params);
    args.pjdesc = pd_usrset;
    args.prj_hdl = frameworks;
    params_obj->decoder_json_hdl = jsparam_create(&args);
    if(NULL == params_obj->decoder_json_hdl)
    {
        return -1;
    }
    RPTDBG("after jsparam_create");
    memset(params_obj->decoder_json_filename, 0, OD_MAX_PATH_LEN);    
    strncpy(params_obj->decoder_json_filename, filename, OD_MAX_PATH_LEN - 1);

    jsparam_load(params_obj->decoder_json_hdl, params_obj->decoder_json_filename);
    //jsparam_act_all(params_obj->decoder_json_hdl);
    jsparam_sav(params_obj->decoder_json_hdl, params_obj->decoder_json_filename);

    return 0;
}

void decoder_params_destroy(decoder_params_handle handle)
{
    decoder_params_obj * h =  (decoder_params_obj *)handle;

    if(h->decoder_json_hdl)
    {
        jsparam_delete(h->decoder_json_hdl);
    } 

    if(handle == NULL)
    {
        RPTERR("invalid Pointer\n");
        return;
    }

    free(h);
}

/*_*/


